
package com.zdkj.subway.stm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zdkj.common.security.util.ObjectUtils;
import com.zdkj.subway.stm.dto.in.SwStTvmDailyBillDTO;
import com.zdkj.subway.stm.entity.SwStTvmDailyBill;
import com.zdkj.subway.stm.entity.SwStTvmDailyBillCashPay;
import com.zdkj.subway.stm.entity.SwStTvmDailyBillElectronicPay;
import com.zdkj.subway.stm.mapper.SwStTvmDailyBillMapper;
import com.zdkj.subway.stm.service.SwStTvmDailyBillCashPayService;
import com.zdkj.subway.stm.service.SwStTvmDailyBillElectronicPayService;
import com.zdkj.subway.stm.service.SwStTvmDailyBillService;
import com.zdkj.subway.stm.util.BeanUtilsStm;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * TVM日结账单录入
 *
 * @author songtuo
 * @date 2020-03-06 13:50:10
 */
@Service
public class SwStTvmDailyBillServiceImpl extends ServiceImpl<SwStTvmDailyBillMapper, SwStTvmDailyBill> implements SwStTvmDailyBillService {

    @Autowired
    SwStTvmDailyBillCashPayService swStTvmDailyBillCashPayService;

    @Autowired
    SwStTvmDailyBillElectronicPayService swStTvmDailyBillElectronicPayService;

    @Autowired
    SwStTvmDailyBillMapper swStTvmDailyBillMapper;

    @Override
    @Transactional
    public boolean saveSwStTvmDailyBill(SwStTvmDailyBillDTO stTvmDailyBillDTO) {
        //1:复制主表公共属性
        SwStTvmDailyBill swStTvmDailyBill = stTvmDailyBillDTO.getSwStTvmDailyBill();
        SwStTvmDailyBill swStTvmDailyBillObj = ObjectUtils.getDefaultObj(SwStTvmDailyBill.class);
        BeanUtil.copyProperties(swStTvmDailyBillObj, swStTvmDailyBill, CopyOptions.create().setIgnoreNullValue(true));
        //2:复制从表公共属性
        List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays = stTvmDailyBillDTO.getSwStTvmDailyBillCashPays();
        BeanUtilsStm.buildProperties(swStTvmDailyBillCashPays);
        List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays = stTvmDailyBillDTO.getSwStTvmDailyBillElectronicPays();
        BeanUtilsStm.buildProperties(swStTvmDailyBillElectronicPays);
        //3:过滤TVM编号 并且计算每个TVM的日结算总金额
        Map<String, BigDecimal> countCashMap = countDailyTotalAmountCashPay(swStTvmDailyBillCashPays);
        Map<String, BigDecimal> countElMap = countDailyTotalAmountElPay(swStTvmDailyBillElectronicPays);
        //4:入库主表
        Map<String, Integer> relationIdsMap = getRelationIdsMap(swStTvmDailyBill, countCashMap, countElMap);
        //5:保存从表
        saveSwTvmBillSun(relationIdsMap, swStTvmDailyBillCashPays, swStTvmDailyBillElectronicPays);
        return true;
    }

    //保存从表
    private void saveSwTvmBillSun(Map<String, Integer> relationIdsMap, List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays, List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays) {
        //现金支付表入库
        if (CollectionUtil.isNotEmpty(swStTvmDailyBillCashPays)) {
            swStTvmDailyBillCashPays.forEach(sw -> sw.setRelationId(relationIdsMap.get(sw.getTvmCode())));
            swStTvmDailyBillCashPayService.saveBatch(swStTvmDailyBillCashPays);
        }
        //电子支付表入库
        if (CollectionUtil.isNotEmpty(swStTvmDailyBillElectronicPays)) {
            swStTvmDailyBillElectronicPays.forEach(sw -> sw.setRelationId(relationIdsMap.get(sw.getTvmCode())));
            swStTvmDailyBillElectronicPayService.saveBatch(swStTvmDailyBillElectronicPays);
        }
    }

    private Map<String, Integer> getRelationIdsMap(SwStTvmDailyBill swStTvmDailyBill, Map<String, BigDecimal> countCashMap, Map<String, BigDecimal> countElMap) {
        //两个表的数据不一样的时候应该取并集
        Set<String> tvmCodeSet = new HashSet<>();
        tvmCodeSet.addAll(countCashMap.keySet());
        tvmCodeSet.addAll(countElMap.keySet());
        Map<String, Integer> relationIdsMap = new HashMap<>();
        for (String tvmCode : tvmCodeSet) {
            SwStTvmDailyBill sw = new SwStTvmDailyBill();
            BeanUtil.copyProperties(swStTvmDailyBill, sw, CopyOptions.create().setIgnoreNullValue(true));
            sw.setTvmCode(tvmCode);
            sw.setOperatorId(sw.getCreatorId());
            sw.setOperatorName(sw.getCreatorName());
            if (countCashMap.get(tvmCode) != null) {
                sw.setDailyCashTotalAmount(countCashMap.get(tvmCode));
            }
            if (countElMap.get(tvmCode) != null) {
                sw.setElectronicPayTotalAmount(countElMap.get(tvmCode));
            }
            save(sw);
            relationIdsMap.put(tvmCode, sw.getId());
        }
        return relationIdsMap;
    }

    /**
     * 统计 日结信息 key tvmcode  value 是日营收总额
     *
     * @param swStTvmDailyBillCashPays 现金支付
     * @return
     */
    private Map<String, BigDecimal> countDailyTotalAmountCashPay(List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays) {
        Map<String, BigDecimal> countMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(swStTvmDailyBillCashPays)) {
            for (SwStTvmDailyBillCashPay swCashPay : swStTvmDailyBillCashPays) {
                String tvmCode = swCashPay.getTvmCode();
                if (countMap.get(tvmCode) == null) {
                    BigDecimal dailyTotalAmountInit = new BigDecimal(0D);
                    BigDecimal dailyTotalAmount = countDailyTotalAmountCash(swCashPay, dailyTotalAmountInit);
                    countMap.put(tvmCode, dailyTotalAmount.doubleValue() == 0 ? null : dailyTotalAmount);
                } else {
                    BigDecimal bigDecimal = countMap.get(tvmCode);
                    BigDecimal dailyTotalAmount = countDailyTotalAmountCash(swCashPay, bigDecimal);
                    countMap.put(tvmCode, dailyTotalAmount);
                }
            }
        }
        return countMap;
    }
    /**
     * 统计 日结信息 key tvmcode  value 是日营收总额
     *
     * @param swStTvmDailyBillElectronicPays 电子支付
     * @return
     */
    private Map<String, BigDecimal> countDailyTotalAmountElPay(List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays) {
        Map<String, BigDecimal> countMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(swStTvmDailyBillElectronicPays)) {
            for (SwStTvmDailyBillElectronicPay swPay : swStTvmDailyBillElectronicPays) {
                String tvmCode = swPay.getTvmCode();
                if (countMap.get(tvmCode) == null) {
                    BigDecimal dailyTotalAmountInit = new BigDecimal(0D);
                    BigDecimal dailyTotalAmount = countDailyTotalAmountEL(swPay, dailyTotalAmountInit);
                    //默认值给成null 0有歧义
                    countMap.put(tvmCode, dailyTotalAmount.doubleValue() == 0 ? null : dailyTotalAmount);
                } else {
                    BigDecimal bigDecimal = countMap.get(tvmCode);
                    BigDecimal dailyTotalAmount = countDailyTotalAmountEL(swPay, bigDecimal);
                    countMap.put(tvmCode, dailyTotalAmount);
                }
            }
        }
        return countMap;
    }

    /**
     * 2020 3-6 客户澄清算法
     *2020.10.28 售票+充值-故障 客户澄清算法
     * @param swCashPay
     * @param dailyTotalAmountInit
     * @return
     */
    private BigDecimal countDailyTotalAmountCash(SwStTvmDailyBillCashPay swCashPay, BigDecimal dailyTotalAmountInit) {
        //售票总金额 sellTicketTotalMoney
        BigDecimal receiveMoney = swCashPay.getSellTicketTotalMoney();
        if (null != receiveMoney) {
            dailyTotalAmountInit = dailyTotalAmountInit.add(receiveMoney);
        }
        //充值总金额 rechargeMoney
        BigDecimal changeTotalMoney = swCashPay.getRechargeMoney();
        if (null != changeTotalMoney) {
            dailyTotalAmountInit = dailyTotalAmountInit.add(changeTotalMoney);
        }
        //故障退款总金额
        BigDecimal faultRefundAmount = swCashPay.getFaultRefundAmount();
        if (null != faultRefundAmount) {
            dailyTotalAmountInit = dailyTotalAmountInit.add(faultRefundAmount);
        }
        return dailyTotalAmountInit;
    }

    private BigDecimal countDailyTotalAmountEL(SwStTvmDailyBillElectronicPay swPay, BigDecimal dailyTotalAmount) {
        //售票总金额
        BigDecimal sellTicketMoney = swPay.getSellTicketMoney();
        if (null != sellTicketMoney) {
            dailyTotalAmount = dailyTotalAmount.add(sellTicketMoney);
        }
        //充值总金额
        BigDecimal rechargeMoney = swPay.getRechargeMoney();
        if (null != rechargeMoney) {
            dailyTotalAmount = dailyTotalAmount.add(rechargeMoney);
        }
        //故障退款总金额
        BigDecimal faultRefundAmount = swPay.getFaultRefundAmount();
        if (null != faultRefundAmount) {
            dailyTotalAmount = dailyTotalAmount.subtract(faultRefundAmount);
        }
        return dailyTotalAmount;
    }

    /**
     * 更新日结账单详情
     *
     * @param swStTvmDailyBillDTO
     * @return 存在这么一种场景 假设  TVM 10台  现金表录入了十台  电子表手动添加的时候只录入了9台 或者一天暂时录入不了 忘了统计
     * 等等场景  所以更新的时候  也是支持用户的新增的
     */
    @Override
    @Transactional
    public boolean updateSwStTvmDailyBill(SwStTvmDailyBillDTO swStTvmDailyBillDTO) {
        SwStTvmDailyBill swStTvmDailyBill = swStTvmDailyBillDTO.getSwStTvmDailyBill();
        Integer rid = swStTvmDailyBill.getId();
        //更新从表信息
        List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays = swStTvmDailyBillDTO.getSwStTvmDailyBillCashPays();
        updateCashPays(rid, swStTvmDailyBillCashPays);
        List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays = swStTvmDailyBillDTO.getSwStTvmDailyBillElectronicPays();
        updateElPays(rid, swStTvmDailyBillElectronicPays);
        //更新主表信息 更新主表信息时候 需要重新计算
        Map<String, BigDecimal> countCashMap = countDailyTotalAmountCashPay(swStTvmDailyBillCashPays);
        Map<String, BigDecimal> countElMap = countDailyTotalAmountElPay(swStTvmDailyBillElectronicPays);
        swStTvmDailyBill.setDailyCashTotalAmount(countCashMap.get(swStTvmDailyBill.getTvmCode()));
        swStTvmDailyBill.setElectronicPayTotalAmount(countElMap.get(swStTvmDailyBill.getTvmCode()));
        updateById(swStTvmDailyBill);
        return true;
    }

    private void updateElPays(Integer rid, List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays) {
        swStTvmDailyBillElectronicPays.forEach(x -> x.setRelationId(rid));
        List<Integer> wIds = swStTvmDailyBillElectronicPays.stream().map(SwStTvmDailyBillElectronicPay::getId).collect(Collectors.toList());
        List<SwStTvmDailyBillElectronicPay> wList = swStTvmDailyBillElectronicPayService.list(new QueryWrapper<SwStTvmDailyBillElectronicPay>().lambda().eq(SwStTvmDailyBillElectronicPay::getRelationId, rid));
        List<Integer> orginWIds = wList.stream().map(SwStTvmDailyBillElectronicPay::getId).collect(Collectors.toList());
        Collection<Integer> removeWIds = CollectionUtils.subtract(orginWIds, wIds);
        if (CollectionUtils.isNotEmpty(swStTvmDailyBillElectronicPays)) {
            swStTvmDailyBillElectronicPayService.saveOrUpdateBatch(swStTvmDailyBillElectronicPays);
        }
        if (CollectionUtils.isNotEmpty(removeWIds)) {
            swStTvmDailyBillElectronicPayService.removeByIds(removeWIds);
        }
    }

    private void updateCashPays(Integer rid, List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays) {
        swStTvmDailyBillCashPays.forEach(x -> x.setRelationId(rid));
        List<Integer> wIds = swStTvmDailyBillCashPays.stream().map(SwStTvmDailyBillCashPay::getId).collect(Collectors.toList());
        List<SwStTvmDailyBillCashPay> wList = swStTvmDailyBillCashPayService.list(new QueryWrapper<SwStTvmDailyBillCashPay>().lambda().eq(SwStTvmDailyBillCashPay::getRelationId, rid));
        List<Integer> orginWIds = wList.stream().map(SwStTvmDailyBillCashPay::getId).collect(Collectors.toList());
        Collection<Integer> removeWIds = CollectionUtils.subtract(orginWIds, wIds);
        if (CollectionUtils.isNotEmpty(removeWIds)) {
            swStTvmDailyBillCashPayService.removeByIds(removeWIds);
        }
        if (CollectionUtils.isNotEmpty(swStTvmDailyBillCashPays)) {
            swStTvmDailyBillCashPayService.saveOrUpdateBatch(swStTvmDailyBillCashPays);
        }
    }

    @Override
    @Transactional
    public boolean deleteCascadeById(Integer id) {
        //删除主表 现在采用的软删除的方式
        removeById(id);
        //删除从表
        swStTvmDailyBillCashPayService.remove(new QueryWrapper<SwStTvmDailyBillCashPay>().lambda().eq(SwStTvmDailyBillCashPay::getRelationId, id));
        swStTvmDailyBillElectronicPayService.remove(new QueryWrapper<SwStTvmDailyBillElectronicPay>().lambda().eq(SwStTvmDailyBillElectronicPay::getRelationId, id));
        return true;
    }

    @Override
    public SwStTvmDailyBillDTO queryDetailsByid(Integer id) {
        SwStTvmDailyBillDTO swStTvmDailyBillDTO = new SwStTvmDailyBillDTO();
        SwStTvmDailyBill swStTvmDailyBill = getById(id);
        swStTvmDailyBillDTO.setSwStTvmDailyBill(swStTvmDailyBill);
        List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays = swStTvmDailyBillCashPayService.list(new QueryWrapper<SwStTvmDailyBillCashPay>().lambda().eq(SwStTvmDailyBillCashPay::getRelationId, id));
        swStTvmDailyBillDTO.setSwStTvmDailyBillCashPays(swStTvmDailyBillCashPays);
        List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays = swStTvmDailyBillElectronicPayService.list(new QueryWrapper<SwStTvmDailyBillElectronicPay>().lambda().eq(SwStTvmDailyBillElectronicPay::getRelationId, id));
        swStTvmDailyBillDTO.setSwStTvmDailyBillElectronicPays(swStTvmDailyBillElectronicPays);
        return swStTvmDailyBillDTO;
    }

    @Override
    public boolean updateSwStTvmDailyBillStatus(List<Integer> ids, Integer status) {
        return swStTvmDailyBillMapper.updateSwStTvmDailyBill(ids, status);
    }

    @Override
    @Transactional
    public boolean saveOrUpdteSwStTvmDailyBill(SwStTvmDailyBillDTO swStTvmDailyBillDTO) {
        SwStTvmDailyBill swStTvmDailyBill = swStTvmDailyBillDTO.getSwStTvmDailyBill();
        /*List<SwStTvmDailyBill> existsList = list(Wrappers.<SwStTvmDailyBill>lambdaQuery()
                .eq(SwStTvmDailyBill::getLineCode, swStTvmDailyBill.getLineCode())
                .eq(SwStTvmDailyBill::getStationCode, swStTvmDailyBill.getStationCode())
                .eq(SwStTvmDailyBill::getTvmCode, swStTvmDailyBill.getTvmCode())
                .eq(SwStTvmDailyBill::getStartTime, swStTvmDailyBill.getStartTime())
                .eq(SwStTvmDailyBill::getEndTime, swStTvmDailyBill.getEndTime())
                .eq(SwStTvmDailyBill::getDelFlag, swStTvmDailyBill.getDelFlag()));
        if(existsList.size() > 0){
            throw new RuntimeException("该TVM日结账单数据已录入");
        }*/
        SwStTvmDailyBill swStTvmDailyBillObj = ObjectUtils.getDefaultObj(SwStTvmDailyBill.class);
        BeanUtil.copyProperties(swStTvmDailyBillObj, swStTvmDailyBill, CopyOptions.create().setIgnoreNullValue(true));
        LocalDateTime startTime = swStTvmDailyBill.getStartTime();
        LocalDateTime endTime = swStTvmDailyBill.getEndTime();
        List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays = swStTvmDailyBillDTO.getSwStTvmDailyBillCashPays();
        //保存支持多个的能力  当前一个一个传也是支持的
        if (CollectionUtils.isNotEmpty(swStTvmDailyBillCashPays)) {
            //统计  按照tvm进行分组
            Map<String, List<SwStTvmDailyBillCashPay>> countMap = swStTvmDailyBillCashPays.stream().collect(Collectors.groupingBy(swKey -> swKey.getTvmCode()));
            Set<String> tvmSet = countMap.keySet();
            for (String tvmCode : tvmSet) {
                //根据tvm查询主表 如果查到了直接更新 查不到的话直接点击添加即可
                SwStTvmDailyBill mainTable = getMainTable(swStTvmDailyBill, startTime, tvmCode, endTime);
                if (null != mainTable) {
                    //复用以前接口 查询主表详情 并更新主表详情
                    SwStTvmDailyBillDTO tvmDto = queryDetailsByid(mainTable.getId());
                    tvmDto.setSwStTvmDailyBillCashPays(countMap.get(tvmCode));
                    /*if (tvmDto.getSwStTvmDailyBillCashPays() != null) {
                        throw new RuntimeException("tvm-" + tvmCode + "in date startTime" + swStTvmDailyBill.getStartTime() +"endTime "+swStTvmDailyBill.getEndTime()+ "has already add");
                    }*/
                    updateSwStTvmDailyBill(tvmDto);
                } else {
                    SwStTvmDailyBillDTO saveTto = new SwStTvmDailyBillDTO();
                    saveTto.setSwStTvmDailyBillCashPays(countMap.get(tvmCode));
                    saveTto.setSwStTvmDailyBill(swStTvmDailyBill);
                    saveSwStTvmDailyBill(saveTto);
                }
            }
        }
        List<SwStTvmDailyBillElectronicPay> swStTvmDailyBillElectronicPays = swStTvmDailyBillDTO.getSwStTvmDailyBillElectronicPays();
        //保存支持多个的能力  当前一个一个传也是支持的
        if (CollectionUtils.isNotEmpty(swStTvmDailyBillElectronicPays)) {
            //统计  按照tvm进行分组
            Map<String, List<SwStTvmDailyBillElectronicPay>> countMap = swStTvmDailyBillElectronicPays.stream().collect(Collectors.groupingBy(swKey -> swKey.getTvmCode()));
            Set<String> tvmSet = countMap.keySet();
            for (String tvmCode : tvmSet) {
                //根据tvm查询主表 如果查到了直接更新 查不到的话直接点击添加即可
                SwStTvmDailyBill mainTable = getMainTable(swStTvmDailyBill, startTime, tvmCode, endTime);
                if (null != mainTable) {
                    //复用以前接口 查询主表详情 并更新主表详情
                    SwStTvmDailyBillDTO tvmDto = queryDetailsByid(mainTable.getId());
                    /*if (tvmDto.getSwStTvmDailyBillElectronicPays() != null) {
                        throw new RuntimeException("tvm-" + tvmCode + "in date startTime" + swStTvmDailyBill.getStartTime() +"endTime "+swStTvmDailyBill.getEndTime()+ "has already add");
                    }*/
                    tvmDto.setSwStTvmDailyBillElectronicPays(countMap.get(tvmCode));
                    updateSwStTvmDailyBill(tvmDto);
                } else {
                    SwStTvmDailyBillDTO saveTto = new SwStTvmDailyBillDTO();
                    saveTto.setSwStTvmDailyBillElectronicPays(countMap.get(tvmCode));
                    saveTto.setSwStTvmDailyBill(swStTvmDailyBill);
                    saveSwStTvmDailyBill(saveTto);
                }
            }
        }
        return true;
    }

    @Override
    public boolean updateOnePay(SwStTvmDailyBillDTO stTvmDailyBillDTO) {
        SwStTvmDailyBill swStTvmDailyBill = stTvmDailyBillDTO.getSwStTvmDailyBill();
        List<SwStTvmDailyBillCashPay> swStTvmDailyBillCashPays = stTvmDailyBillDTO.getSwStTvmDailyBillCashPays();
        if (CollectionUtils.isNotEmpty(swStTvmDailyBillCashPays)) {
            SwStTvmDailyBillDTO tvmDto = queryDetailsByid(swStTvmDailyBill.getId());
            tvmDto.setSwStTvmDailyBill(swStTvmDailyBill);
            tvmDto.setSwStTvmDailyBillCashPays(swStTvmDailyBillCashPays);
            updateSwStTvmDailyBill(tvmDto);
        }
        List<SwStTvmDailyBillElectronicPay> electronicPays = stTvmDailyBillDTO.getSwStTvmDailyBillElectronicPays();
        if (CollectionUtils.isNotEmpty(electronicPays)) {
            SwStTvmDailyBillDTO tvmDto = queryDetailsByid(swStTvmDailyBill.getId());
            tvmDto.setSwStTvmDailyBill(swStTvmDailyBill);
            tvmDto.setSwStTvmDailyBillElectronicPays(electronicPays);
            updateSwStTvmDailyBill(tvmDto);
        }
        if (CollectionUtils.isEmpty(swStTvmDailyBillCashPays) && CollectionUtils.isEmpty(electronicPays)) {
            updateById(swStTvmDailyBill);
        }
        return false;
    }

    /**
     * 查询主表存在不存在
     *
     * @param swStTvmDailyBill
     * @param startTime
     * @param tvmCode
     * @param endTime
     * @return
     */
    private SwStTvmDailyBill getMainTable(SwStTvmDailyBill swStTvmDailyBill, LocalDateTime startTime, String tvmCode, LocalDateTime endTime) {
        return getOne(new QueryWrapper<SwStTvmDailyBill>().lambda().eq(SwStTvmDailyBill::getLineCode, swStTvmDailyBill.getLineCode())
                .eq(SwStTvmDailyBill::getStationCode, swStTvmDailyBill.getStationCode())
                .eq(SwStTvmDailyBill::getTvmCode, tvmCode).eq(SwStTvmDailyBill::getStartTime, startTime).eq(SwStTvmDailyBill::getEndTime, endTime));
    }

}
